#include <QDebug>
#include <QPainter>
#include <QInputDialog>
#include "ImagePanel.h"
#include "DrawCommand.h"
#include "SettingHelper.h"

const float fPerDelta(0.005f);
const float fZoomMin(0.01f);
const float fZoomMax(5.f);

ImagePanel::ImagePanel(QWidget *parent) :
	IImagePanel(parent), m_pShapeTemp(0), m_pImage(0), m_fZoomRatio(1.f),
	m_fLastZoom(1.f), m_bSelected(false), m_undoStack(this),
	m_pListShape(new QList<IShape *>), m_fPixelPitchH(1.f), m_fPixelPitchV(1.f)
{
	this->setAcceptDrops(true);
	this->setFocusPolicy(Qt::WheelFocus);
	this->installEventFilter(this);
}

ImagePanel::~ImagePanel()
{
	this->resetImage();
	this->resetOverlay();
}

void ImagePanel::setImagePath(QString strFilepath)
{
	this->resetImage();
	this->resetOverlay();
	m_pImage = new RawImage(strFilepath);

	double h, v, z;

	SettingHelper::loadCalibration(h, v);
	SettingHelper::loadZoomRatio(z);
	m_fPixelPitchH = h * z * (m_pImage->width() == iBinningModeWidth ? 1. : 0.5);
	m_fPixelPitchV = v * z * (m_pImage->width() == iBinningModeWidth ? 1. : 0.5);
	this->calcFitSize();
	emit SignalSelected();
}

void ImagePanel::setSelect(bool bSelect)
{
	this->m_bSelected = bSelect;

	if(bSelect)
	{
		int iWWMax(0), iWWMin(0), iWWidth(0), iWCenter(0);
		QMap<int, int> histogramData;

		if(m_pImage)
		{
			histogramData = m_pImage->histogramData();
			m_pImage->windowing(iWWMax, iWWMin, iWWidth, iWCenter);
		}

		emit SignalHistory(m_undoStack.createUndoAction(this), m_undoStack.createRedoAction(this));
		emit SignalWindowingValue(iWWMax, iWWMin, iWWidth, iWCenter);
		emit SignalHistogramData(histogramData);
	}

	this->update();
}

void ImagePanel::reset()
{
	this->resetOverlay();
	this->update();
}

void ImagePanel::SlotWindowing(int width, int center)
{
	if(m_pImage && !m_pImage->isNull())
	{
		m_pImage->setWindowing(width, center);
		this->update();
	}
}

void ImagePanel::SlotGamma(int gamma)
{
	if(m_pImage && !m_pImage->isNull())
	{
		m_pImage->setGamma(gamma);
		this->update();
	}
}

void ImagePanel::SlotSetTool(ETool eTool)
{
	m_eTool = eTool;

	IShape *pShape(0);

	switch (eTool)
	{
	case eToolOval:
		pShape = new Oval;
		break;
	case eToolRect:
		pShape = new Rect;
		break;
	case eToolLine:
		pShape = new Line;
		break;
	case eToolPen:
		pShape = new Pen;
		break;
	case eToolText:
		pShape = new Text;
		break;
	case eToolLength:
		pShape = new Length(m_fPixelPitchH, m_fPixelPitchV);
		break;
	case eToolZoom:
		break;
	case eToolProfile:
		pShape = new Profile(m_fPixelPitchH, m_fPixelPitchV);
		break;
	case eToolPosition:
		pShape = new Position;
	default:
		break;
	}

	if(m_pShapeTemp) delete m_pShapeTemp;

	m_pShapeTemp = pShape;
}

void ImagePanel::saveImage(bool bIsDiagnosis)
{
	if(bIsDiagnosis)
	{
		QString savePath, saveExt;
		SettingHelper::loadDiagnosis(&savePath, &saveExt);

		QFileInfo fi(savePath);

		if(fi.exists() && fi.isFile())
		{
			QDir dir = fi.dir();

			m_pImage->save(dir.absoluteFilePath(QString("%1.%2").arg(strDiagnosisImgName, saveExt)));
		}
	}
	else
	{
		QImage saveBuffer(*m_pImage);
		QPainter painter(&saveBuffer);

		foreach (IShape *pShape, *m_pListShape)
		{
			pShape->draw(painter, 1.f);
		}

		QDir dir("save");
		dir.mkpath(".");

		saveBuffer.save(QString("%1/%2.png").arg(dir.dirName(), QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss")));
	}
}

void ImagePanel::paintEvent(QPaintEvent *event)
{
	Q_UNUSED(event);

	QPainter painter(this);

	painter.fillRect(this->rect(), Qt::black);

	if(m_pImage && !m_pImage->isNull())
	{
		QRectF rect(m_pImage->rect());

		rect.setSize(rect.size() * m_fZoomRatio);
		painter.setRenderHint(QPainter::Antialiasing);
		painter.drawImage(rect, *m_pImage);

		foreach (IShape *pShape, *m_pListShape)
		{
			pShape->draw(painter, m_fZoomRatio);
		}

		if(m_pShapeTemp && m_pShapeTemp->size() > 0) m_pShapeTemp->draw(painter, m_fZoomRatio);
	}

	painter.save();
	if(m_bSelected) painter.setPen(QPen(Qt::red, 2));
	else painter.setPen(QPen(Qt::black, 2));
	painter.drawRect(this->rect());
	painter.restore();
}

void ImagePanel::mousePressEvent(QMouseEvent *event)
{
	emit SignalSelected();

	if(event->buttons() == Qt::LeftButton && m_pImage && !m_pImage->isNull())
	{
		m_ptStart = event->pos();

		if(m_pShapeTemp) m_pShapeTemp->addPoint(m_ptStart / m_fZoomRatio);

		this->update();
	}

}

void ImagePanel::mouseMoveEvent(QMouseEvent *event)
{
	if(event->buttons() == Qt::LeftButton && m_pImage && !m_pImage->isNull())
	{
		m_ptEnd = event->pos();

		switch (m_eTool)
		{
		case eToolZoom:
			this->updateZoomRatio();
			break;
		default:
			break;
		}

		if(m_pShapeTemp) m_pShapeTemp->addPoint(m_ptEnd / m_fZoomRatio);

		switch (m_eTool)
		{
		case eToolProfile:
		{
			Profile *pProfile = static_cast<Profile*>(m_pShapeTemp);

			if(pProfile)
			{
				const QList<QPoint> listPt = pProfile->pointList();
				const QList<int> listProfile = m_pImage->profileData(listPt);
				emit SignalProfileDraw(listProfile);
			}
		}
			break;
		default:
			break;
		}

		this->update();
	}
}

void ImagePanel::mouseReleaseEvent(QMouseEvent *event)
{
	Q_UNUSED(event);

	if(m_pImage && !m_pImage->isNull())
	{
		if(m_pShapeTemp)
		{
			switch (m_eTool)
			{
			case eToolText:
			{
				bool bOk;
				QString strText = QInputDialog::getText(this, QCoreApplication::applicationName(), "코멘트 추가",
														QLineEdit::Normal, QString(), &bOk);
				if(bOk) m_pShapeTemp->setText(strText);
			}
				break;
			default:
				break;
			}

			if(m_pShapeTemp->size() > 5)
			{
				m_undoStack.push(new DrawCommand(this, m_pListShape, m_pShapeTemp));

				emit SignalHistory(m_undoStack.createUndoAction(this), m_undoStack.createRedoAction(this));

			}
			else delete m_pShapeTemp;

			m_pShapeTemp = NULL;
			this->SlotSetTool(m_eTool);
		}
		else
		{
			switch (m_eTool)
			{
			case eToolZoom:
				this->applyZoomRatio();
				break;
			default:
				break;
			}
		}
	}
}

void ImagePanel::resizeEvent(QResizeEvent *event)
{
	QWidget::resizeEvent(event);

	this->calcFitSize();
	this->update();
}

void ImagePanel::dragEnterEvent(QDragEnterEvent *event)
{
	if(event->mimeData()->hasUrls())
	{
		event->acceptProposedAction();
	}
}

void ImagePanel::dropEvent(QDropEvent *event)
{
	if(event->mimeData()->hasUrls())
	{
		event->acceptProposedAction();

		QList<QUrl> listUrl = event->mimeData()->urls();

		if(listUrl.count() > 0) this->setImagePath(listUrl[0].toLocalFile());
	}
}

void ImagePanel::mouseDoubleClickEvent(QMouseEvent *event)
{
	emit SignalDoubleClick();
}

bool ImagePanel::eventFilter(QObject *o, QEvent *e)
{
	if(o == this && e->type() == QEvent::Wheel)
	{
		QWheelEvent *event = static_cast<QWheelEvent*>(e);

		if(event)
		{
			switch (m_eTool)
			{
			case eToolZoom:
				m_fZoomRatio = qBound(fZoomMin,
									  m_fZoomRatio + (event->delta() > 0 ? 1 : -1) * fPerDelta,
									  fZoomMax);
				this->applyZoomRatio();
				this->update();

				break;
			default:
				break;
			}
		}

		return true;
	}
	else return IImagePanel::eventFilter(o, e);
}

void ImagePanel::updateZoomRatio()
{
	int delta = (m_ptStart - m_ptEnd).y();

	m_fZoomRatio = qBound(fZoomMin, m_fLastZoom + delta * fPerDelta, fZoomMax);
	qDebug() << "[update zoom]" << delta << m_fLastZoom << m_fZoomRatio;
}

void ImagePanel::applyZoomRatio()
{
	m_fLastZoom = m_fZoomRatio;
	qDebug() << "[apply zoom]" << m_fLastZoom << m_fZoomRatio;
}

void ImagePanel::calcFitSize()
{
	float ratio(1.f);

	if(m_pImage)
	{
		QSize sizeImg(m_pImage->size());
		QSize size(m_pImage->size().scaled(this->size(), Qt::KeepAspectRatio));

		ratio = ratio * size.width() / sizeImg.width();
	}

	m_fZoomRatio = m_fLastZoom = ratio;
}

void ImagePanel::resetImage()
{
	if(m_pImage)
	{
		delete m_pImage;
		m_pImage = NULL;
	}
}

void ImagePanel::resetOverlay()
{
	while(!m_pListShape->isEmpty()) delete m_pListShape->takeFirst();

	if(m_pShapeTemp)
	{
		delete m_pShapeTemp;
		m_pShapeTemp = NULL;
	}
}
